﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace XModem.library
{
    

    public class Modem
    {
        /// <summary>
        /// xmodem 类型
        /// </summary>
        public enum XModemType
        {
            XModem128,
            Xmodem1k
        }
        protected static byte SOH = 0x01; /* 标题开头 */
        protected static  byte STX = 0x02; /* 文本开头（与SOH类似，但表示1024块大小） */
        protected static  byte EOT = 0x04; /* 终端传输结束 */
        protected static  byte ACK = 0x06; /* 终端立即确认 */
        protected static  byte NAK = 0x15; 
        protected static  byte CAN = 0x18; /* 客户端取消 */

        protected static  byte CPMEOF = 0x1A; /* 补齐字符 */
        protected static  byte ST_C =(byte)'C';

        protected static  int MAXERRORS = 10;

        protected static  int BLOCK_TIMEOUT = 1000;
        protected static  int REQUEST_TIMEOUT = 3000;
        protected static  int WAIT_FOR_RECEIVER_TIMEOUT = 60000;
        protected static  int SEND_BLOCK_TIMEOUT = 10000;
        

        private SerialPort sPort;//端口

        private XModemType sxmodemType;
        
        /// <summary>
        /// 用来结束循环
        /// </summary>
        public bool isStopModem = true;

        /// <summary>
        /// 错误定义委托
        /// </summary>
        /// <param name="msg"></param>
        public delegate void SendEventMsg(string msg);
        /// <summary>
        /// 错误定义事件
        /// </summary>
        public event SendEventMsg SendMsg;


        #region 重写逻辑代码

        public Modem(SerialPort _sPort, XModemType type)
        {
            sPort = _sPort;
            sxmodemType = type;
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="path"></param>
        /// <param name="useBlock1K"></param>
        public void XModemSend( bool useBlock1K) {
            try
            {
                outMsg("烧写创建。。。");
                //包序号
                int PackageTemp = 0;
                int useCRC = -1 ;
                while (isStopModem)
                {
                    byte rbyte= readByte();

                    switch (rbyte)
                    {
                        case 67:
                            {
                                useCRC = 0;
                                //发送c 端代码
                                if (useBlock1K)
                                {
                                    if (PackageTemp== XmodemData.xmodem1k_C.Count -1)
                                    {
                                        //发送取消命令
                                        SendCmdEnd();
                                        isStopModem = false;
                                    }
                                    sPort.Write(XmodemData.xmodem1k_C[PackageTemp].ToArray(), 0, XmodemData.xmodem1k_C[PackageTemp].ToArray().Count());
                                    
                                }
                                else {
                                    if (PackageTemp == XmodemData.xmodem_C.Count - 1)
                                    {
                                        //发送取消命令
                                        SendCmdEnd();
                                        isStopModem = false;
                                    }
                                    sPort.Write(XmodemData.xmodem_C[PackageTemp].ToArray(),0, XmodemData.xmodem_C[PackageTemp].ToArray().Count());
                                }
                            }
                            break;
                        case 21:
                            {
                                useCRC = 1;
                                //发送c 端代码
                                if (useBlock1K)
                                {
                                    if (PackageTemp == XmodemData.xmodem1k_N.Count - 1)
                                    {
                                        //发送取消命令
                                        SendCmdEnd();
                                        isStopModem = false;
                                    }

                                    sPort.Write(XmodemData.xmodem1k_N[PackageTemp].ToArray(), 0, XmodemData.xmodem1k_N[PackageTemp].ToArray().Count());
                                }
                                else
                                {
                                    if (PackageTemp == XmodemData.xmodem_N.Count - 1)
                                    {
                                        //发送取消命令
                                        SendCmdEnd();
                                        isStopModem = false;
                                    }
                                    sPort.Write(XmodemData.xmodem_N[PackageTemp].ToArray(), 0, XmodemData.xmodem_N[PackageTemp].ToArray().Count());
                                }
                            }
                            break;
                        case 06:
                            {
                                outMsg(string.Format("正在努力烧写第{0}包数据。。。", PackageTemp));
                                PackageTemp++;
                                switch (useCRC)
                                {
                                    case 0:
                                        {
                                            //发送c 端代码
                                            if (useBlock1K)
                                            {
                                                if (PackageTemp == XmodemData.xmodem1k_C.Count - 1)
                                                {
                                                    //发送取消命令
                                                    SendCmdEnd();
                                                    isStopModem = false;
                                                }
                                                sPort.Write(XmodemData.xmodem1k_C[PackageTemp].ToArray(), 0, XmodemData.xmodem1k_C[PackageTemp].ToArray().Count());

                                            }
                                            else
                                            {
                                                if (PackageTemp == XmodemData.xmodem_C.Count - 1)
                                                {
                                                    //发送取消命令
                                                    SendCmdEnd();
                                                    isStopModem = false;
                                                }
                                                sPort.Write(XmodemData.xmodem_C[PackageTemp].ToArray(), 0, XmodemData.xmodem_C[PackageTemp].ToArray().Count());
                                            }
                                        }
                                        break;
                                    case 1:
                                        {
                                            if (useBlock1K)
                                            {
                                                if (PackageTemp == XmodemData.xmodem1k_N.Count - 1)
                                                {
                                                    //发送取消命令
                                                    SendCmdEnd();
                                                    isStopModem = false;
                                                }

                                                sPort.Write(XmodemData.xmodem1k_N[PackageTemp].ToArray(), 0, XmodemData.xmodem1k_N[PackageTemp].ToArray().Count());
                                            }
                                            else
                                            {
                                                if (PackageTemp == XmodemData.xmodem_N.Count - 1)
                                                {
                                                    //发送取消命令
                                                    SendCmdEnd();
                                                    isStopModem = false;
                                                }
                                                sPort.Write(XmodemData.xmodem_N[PackageTemp].ToArray(), 0, XmodemData.xmodem_N[PackageTemp].ToArray().Count());
                                            }
                                        }
                                        break;
                                    default:
                                        break;
                                }
                            }
                            break;
                        case 24:
                            {
                                outMsg("错误连接断开");
                                isStopModem = false;
                            }
                            break;
                        default:
                            break;
                    }

                }
                

            }
            catch (Exception ex)
            {
                outMsg(ex.Message.ToString());
            }
        }
        /// <summary>
        /// 发送结束指令
        /// </summary>
        private void SendCmdEnd() {
            sPort.Write(new byte[] { EOT }, 0, 1);
            byte rbyte = readByte();
            if (rbyte != 06)
            {
                sPort.Write(new byte[] { EOT }, 0, 1);
            }
        }
        /// <summary>
        /// 输出错误星系
        /// </summary>
        /// <param name="msg"></param>
        private void outMsg(string msg) {
            if (null != SendMsg)
            {
                SendMsg(msg); 
            }
        }

        /// <summary>
        /// 判断是什么模式
        /// </summary>
        /// <returns></returns>
        public bool waitReceiverRequest()
        {
            byte character = (byte)sPort.ReadByte();
            if (character == NAK)
            {
                return false;
            }
            else if (character == ST_C)
            {
                return true;
            }
            else
            {
                throw new IOException("读取数据失败！");
            }
        }
        /// <summary>
        /// 读取  后期这里需要添加超时退出
        /// </summary>
        /// <returns></returns>
        public byte readByte() {
           return (byte)sPort.ReadByte();
        }


        public void XModemReceive(string _path, bool useBlock1k) {
            try
            {
                
                //检查通讯是否
                //bool isstart = requestStart(useBlock1k);
                List<List<byte>> receiveList = new List<List<byte>>();
                CRC8 crc8 = new CRC8();
                int errorCount = 0;

                while (isStopModem  && errorCount <=10)
                {

                    #region  循环代码

                    if (receiveList.Count > 0)
                    {
                        #region 成功第一次 发送 06 后面的
                        List<byte> temp = new List<byte>();
                        byte[] tempbyte = new byte[sPort.BytesToRead];
                        for (int i = 0; i < tempbyte.Length; i++) ;
                        sPort.Read(tempbyte, 0, tempbyte.Length); //获取整包数据
                                         
                        #region 暂时不用
                        //if (useBlock1k)
                        //{
                        //    tempbyte = new byte[1024 + 4]; //加上4 刚好是15数据整包数据  加上5 是c数据整包数据
                        //}
                        //else
                        //{
                        //    tempbyte = new byte[128 + 4];
                        //}

                        //if (sPort.BytesToRead == tempbyte.Length)
                        //{
                        //    errorCount = 0;
                        //    sPort.Read(tempbyte, 0, tempbyte.Length); //获取整包数据
                        //}
                        //else
                        //{
                        //    errorCount++;
                        //    var odczytanyZnak = Convert.ToChar(sPort.ReadChar());
                        //    if (odczytanyZnak == (char)4)
                        //    {
                        //        isStopModem = false;//结束循环
                        //        sPort.Write(new byte[] { 06 }, 0, 1); //告诉终端结束这次发送
                        //        continue;
                        //    }

                        //    sPort.DiscardInBuffer();//清理缓存区数据
                        //    sPort.Write(new byte[] { 21 }, 0, 1);
                        //    continue;//数据接收不完整
                        //} 
                        #endregion
                        //Thread.Sleep(100);
                        if (tempbyte[0] == (useBlock1k?2:1)) //如果是1k 等于2 如果是k 就是1
                        {
                            byte lastCrc ;
                            if (useBlock1k)
                            {
                                temp.AddRange(tempbyte.Skip(3).Take(1024));
                                lastCrc = tempbyte.Skip(1027).Take(1).First(); //获取校验位
                            }
                            else
                            {
                                temp.AddRange(tempbyte.Skip(3).Take(128));
                                lastCrc = tempbyte.Skip(131).Take(1).First();//获取校验位
                            }
                            //if (tempbyte[1] != receiveList.Count() + 1) //验证包序号是否正确
                            //{
                            //    errorCount++;
                            //    sPort.DiscardInBuffer();//清理缓存区数据
                            //    sPort.Write(new byte[] { 21 }, 0, 1);
                            //    continue; //结束此次循环
                            //}
                            if (lastCrc != BitConverter.GetBytes(crc8.calcCRC(temp.ToArray()))[0]) //判断校验位是否正确
                            {
                                temp = null;
                                errorCount++;
                                sPort.DiscardInBuffer();//清理缓存区数据
                                sPort.Write(new byte[] { 21 }, 0, 1);
                                continue; //结束此次循环
                            }
                            else
                            {
                                receiveList.Add(temp);//添加数据到集合中
                                errorCount = 0;
                            }
                            sPort.Write(new byte[] { 06 }, 0, 1);//这里利用特性 发送06后会发一包数据  循环后发送21有错误次数限制
                        }
                        else if (tempbyte[0] == 4) //表示结束
                        {
                            isStopModem = false;//结束循环
                            sPort.Write(new byte[] { 06 }, 0, 1); //告诉终端结束这次发送
                            continue;
                        }
                        else {
                            sPort.DiscardInBuffer();//清理缓存区数据
                            sPort.Write(new byte[] { 21 }, 0, 1);
                            continue;//数据接收不完整
                        }
                        #endregion
                    }
                    else {

                        #region 初始化业务逻辑
                        List<byte> temp = new List<byte>();
                       
                        sPort.DiscardInBuffer();//清理缓存区数据
                        sPort.Write(new byte[] { 21 }, 0, 1);
                        byte[] tempbyte = new byte[sPort.BytesToRead];
                        for (int i = 0; i < tempbyte.Length; i++) ;
                        sPort.Read(tempbyte, 0, tempbyte.Length); //获取整包数据
                       
                        #region 暂时不用
                        //if (useBlock1k)
                        //{
                        //    tempLength =1028; //加上4 刚好是15数据整包数据  加上5 是c数据整包数据
                        //}
                        //else
                        //{
                        //    tempLength = 132; 
                        //}

                        // if (sPort.BytesToRead == tempLength)
                        //{
                        //    errorCount = 0;
                        //    sPort.Read(tempbyte, 0, tempLength); //获取整包数据
                        //}
                        //else if (sPort.BytesToRead > tempbyte.Length)
                        //{
                        //    errorCount = 0;
                        //    sPort.Read(tempbyte, 0, tempbyte.Length); //获取整包数据
                        //}
                        //else
                        //{
                        //    errorCount++;
                        //    continue;//数据接收不完整
                        //} 
                        #endregion

                        if (tempbyte[0] == (useBlock1k ? 2 : 1))
                        {
                            byte lastCrc ;
                            if (useBlock1k)
                            {
                                temp.AddRange(tempbyte.Skip(3).Take(1024));
                                lastCrc = tempbyte.Skip(1027).Take(1).First(); //获取校验位
                            }
                            else
                            {
                                temp.AddRange(tempbyte.Skip(3).Take(128));
                                lastCrc = tempbyte.Skip(131).Take(1).First();//获取校验位
                            }

                            if (lastCrc != BitConverter.GetBytes(crc8.calcCRC(temp.ToArray()))[0]) //判断校验位是否正确
                            {
                                temp = null;
                                errorCount++;
                                continue; //结束此次循环
                            }
                            else
                            {
                                receiveList.Add(temp);//添加数据到集合中
                                errorCount = 0;
                            }
                            sPort.Write(new byte[] { 06 }, 0, 1);//这里利用特性 发送06后会发一包数据  循环后发送21有错误次数限制
                        }
                        else if (tempbyte[0] == 4) //表示结束
                        {
                            isStopModem = false;//结束循环
                            continue;
                        }
                        else {
                            continue;
                        } 
                        #endregion
                    }

                  
                    #endregion
                    
                }
                if (receiveList.Count()>0)
                {
                    //保存数据到路劲下
                    using (FileStream fs = new FileStream(_path, FileMode.Create))
                    {
                        using (BinaryWriter plik = new BinaryWriter(fs))
                        {
                            foreach (var item in receiveList)
                            {
                                foreach (byte paczka in item)
                                {
                                    plik.Write(paczka);
                                }
                            }
                        }
                    }
                }
               
            }
            catch (Exception ex)
            {

                outMsg(ex.Message.ToString());
            }
           
        }



       
        /// <summary>
        /// 判断通讯是否畅通
        /// </summary>
        /// <param name="useCRC16"></param>
        /// <returns></returns>
        private  bool requestStart(bool useBlock1k) {
            for (int i = 0; i < 10; i++)
            {
                sPort.DiscardInBuffer();//清理缓存区数据
                sPort.Write(new byte[]{ 21 }, 0, 1);
                byte[] tempbyte = new byte[sPort.ReadBufferSize];
                Thread.Sleep(500);
                sPort.Read(tempbyte, 0, tempbyte.Length);
                if (useBlock1k)
                {
                    
                    if (tempbyte[0] ==(byte)2)
                    {
                        return true;
                    }
                }
                else {
                    if (tempbyte[0] ==(byte)1)
                    {
                        return true;
                    }
                }
               
            }
            return false;
        }



        #endregion



    }
}
